\documentclass[12pt]{article}
\usepackage{fullpage}
\usepackage{listings}
\usepackage{courier}
\usepackage[colorlinks=true,urlcolor=blue,linkcolor=black,citecolor=red]{hyperref}

\include{pigasus.lst}

\begin{document}

\begin{titlepage}
\begin{center}

\vspace*{3in}

{\huge Pigasus}

{\it The Tutorial}

\vspace*{0.5in}

COMS W4115

\today

\vfill

\end{center}

\begin{flushright}

{\bf Team Hogzilla}

{\it Project Manager:} John Zhang (jrz2106)

{\it System Architect:} Sharath Gururaj (sa2617)

{\it Language Guru:} Jordan Rupprecht (jjr2119)

{\it Verification and Validation Person:} Philip Tjimos (pmt2103)

{\it System Integrator:} Everyone

\end{flushright}
\end{titlepage}

\tableofcontents
\lstlistoflistings
\newpage

\section{Introduction}

We will present here a brief tutorial for writing a Pigasus program. It is our goal that by reading the tutorial and examining the code, the reader will have sufficient information to use most if not all of the key capabilities of the Pigasus language.

In this tutorial, we will demonstrate the process of establishing connections, distributing jobs and input files, and handling the output of the distributed jobs.

Pigasus is heavily inspired by C, with inspirations from Python. An experienced programmer in either language (or related languages such as C++ and Java) should be able to become comfortable programming with Pigasus quite quickly. While this tutorial will describe general programming constructs such as if-statements, loops, etc., they will not be focused on, and instead we will assume that the user has some experience with common programming languages (or is at least willing to refer to them).

\section{The Pigasus Compiler}

Pigasus code can be written in any regular text editor, and the resultant file must have a ``.pig'' extension. These are then used as input to the pigasus compiler (the \verb|pig| executable). Unless another output filename is provided, the compiler will produce an executable named \verb|pig.out| (get it?).

Therefore, suppose you have the Pigasus code \verb|helloworld.pig|, you can compile and execute as follows from the Unix prompt.
\begin{quote}\begin{verbatim}
$> pig helloworld.pig
$> ls
pig.out
$> ./pig.out
\end{verbatim}\end{quote}
A different output filename can be specified using the \verb|-o| parameter (same as \verb|gcc|).

\section{A First Program}

We will follow computer science tradition and provide an introductory Hello World program.

\subsection{A Simple Hello World}

Here we present a basic program that runs ``Hello, World'' on the remote computer. For now, we will ignore the process of retrieving the output from standard-out.

\begin{lstlisting}[caption=A first ``Hello World'' program.]
job RunHello(list inputs) { 
  @{"echo hello world"};
}

void main(list args) {
  list my_servers = [["host"=>"clic.cs.columbia.edu",
                      "user"=>"jordan",
                      "pass"=>"pigasusR0Cks"
                     ]];
  connect my_servers;
  
  push RunHello, null, null, null;
}
\end{lstlisting}

The beginning of the program defines a \verb|job|, which contains the code that gets executed on the remote machine. The syntax is kept similar to a regular function call, mostly just to make it familiar to experienced programmers. Within the job {\it RunHello}, we do only one thing: run the shell script \verb|echo hello world|. We use the syntax \verb|@{<string>}| to execute something in the shell.

The next part is the {\it main} function. This does the minimal things required to compile a Pigasus program: it declares the servers it wants to use for computing, connects to them, pushes the job out to those computers, and then exits.

The first part of this code declares the {\it my\_servers} variable as a list of maps. These are two distinct data types native to Pigasus:
\begin{description}

\item[list] is a type similar to a \verb|vector| in C++ or a \verb|List| in Java. A unique aspect of \verb|list|s in Pigasus is that it can have elements of different types. In our first example program, we don't use this fact, but it is important to know. Elements in list can be accessed using an integral index.

\item[map] is a type similar to a \verb|map| in C++ or a \verb|Map| in Java. It takes a \verb|string| as an index and returns an object or \verb|null| if there is no such index. As with \verb|list|s, one can have a \verb|map| that contains values of different types, but the keys themselves must all be \verb|string|s.
\end{description}

The {\it my\_servers} list has been filled with only one element, a \verb|map|
that maps important keys for the connection information to the values that
should be used. The intention is that most users will only have to define the
values for the keys ``host'', ``user'', and ``pass''; if someone has other
values that are important, such as ``port'' (if port 22 for SSH is not
available), ``key\_file'' (to use SSH keys instead of a password), or
``priority'' (to give faster servers a weighted priority), they have that
flexibility. 

The next line, \verb|connect my_servers| is a directive to Pigasus, telling it that it should use the \verb|my_servers| servers list when it pushes jobs. Most users will only need to apply this directive once in a program, but there could be instances where one would want to change the global list of servers to use.

Finally, the next line does the actual push of the job. The first argument of \verb|push| is the job name that we want to execute. The next three arguments are for syncing files, specifying inputs, and capturing output. We will illustrate their usage in future sections. Since they are all left as \verb|null|, it is implied that we don't need to sync any files, we want to run it once with no specific inputs, and we do not care about output.

\subsection{Advanced hello}
In this example, we will do the same thing, but show off a few features.

\begin{lstlisting}[caption=A more advanced ``Hello World'' program with file transfer and CSV loading.]
job RunHello(list inputs) { 
  @{"./hello"};
}

void main(list args) {
  list my_servers = loadcsv "servers.txt";
  connect my_servers;
  
  list my_files = ["hello"];
  push RunHello, my_files, null, null;
}
\end{lstlisting}

A few things have changed here, to make it cleaner and more generic. The first change is in the job we use. Instead of running \verb|echo hello world|, we run \verb|./hello|, which is a precompiled program. File \verb|hello| could be a bash script that calls \verb|echo hello world|; it could be a C program that calls \verb|printf("hello world\n");|, etc. All Pigasus cares about is that it is an executable. This will be run on the remote server, so if the \verb|hello| program is not on the remote server, it will result in an error. For now, we assume that it is.

The second change is in the servers definition. In order to make defining servers a little easier, we provide the option of loading in values from a CSV (comma separated values) file. This can be applied to load any list, but it is particularly useful for a list of servers. In fact, we would strongly advise that users keep their server definitions separate in CSV files. This is because Pigasus code, (like any other code) may eventually be shared, and it would be disastrous if one included account passwords in their code which they sent to others. Futhermore, we would even suggest that users of Pigasus generate SSH key files instead of explicitly specifying their passwords in unencrypted text files.

The final change is that, since \verb|hello| is not on the remote server, it must be sent there. We must now provide the second argument of \verb|push|, which is a list of all the files to transfer. Providing this list of files will ensure that before we start any jobs, we send those files to all the servers which we run jobs on.

\section{Distributed Temperature Converter}

Here we consider another program from the text \cite{kernighan:c}, with a Pigasus twist. We implement a Fahrenheit to Celsius converter, sending each calculation to a different machine. This is obviously wasteful with respect to overhead, but it would make sense if the conversion took several hours and could take advantage of multiple cores (and your laptop only has one or two cores).

In this example, we assume that there is a program called \verb|ftoc| which converts a Fahrenheit value to a Celsius value. This can be easily implemented by the bash command \verb\echo ("$1"-32)*5/9 | bc\ (\verb|$1| will assume the value of the first argument).

\begin{lstlisting}[caption=A distributed Fahrenheit to Celsius converter]
job RunFtoC(list inputs) {
  @{"./ftoc %s" % inputs[0]};
}

void main(list args) {
  list my_servers = [["host"=>"clic.cs.columbia.edu",
                      "user"=>"jordan",
                      "pass"=>"pigasusR0Ck"
                     ],
                     ["host"=>"compute.cs.columbia.edu",
                      "user"=>"jjr2119",
                      "pass"=>"example"
                     ]];
  connect my_servers;
  
  list my_ints;
  int i;
  for (i = 0; i < 10; i = i + 1) {
    my_ints = my_ints + [i*20];
  }
  
  list my_files = ["ftoc"];
  
  list output;
  int conversion = push RunFtoC, my_files, my_ints, output;
  wait conversion;
  
  for (i = 0; i < length output; i = i + 1) {
    list out = output[i];
    print "F: %d\t C: %s n" % [my_ints[i][0], out["stdout"]];
  }
}
\end{lstlisting}

We finally utilize all components of the \verb|push| directive: we need to tell it what job to push, we need to send the program that can convert a single Fahrenheit value to Celsius, we need to tell which jobs to run which inputs, and finally, we care about the output.

Since each job needs a list on inputs which it needs to be run with, the \verb|push| directive needs an input list for each job instance, which means that it needs a list of lists. The number of job instances which will be created is equal to the length of the inputs list (with the exception being if the list is \verb|null|). So, we create a list that contains \verb|[[0], [20], [40], ...]|, so that the first job instance will be run with the input list \verb|[0]|, the second job instance will be run with the input list \verb|[20]|, and so on.

To create this input list, we make use of a \verb|for| loop. This is a basic construct used in almost all programming languages. The first section is the initializer, which executes before every loop iteration. This is useful for initializing a loop counter, which we do here. The second section is the test expression, which is evaluated before every loop iteration. If the value of the test expression is \verb|false|, then the for loop stops and the point of execution progresses past the block of code for the for loop. The final section is the post section, which executes after every loop iteration. This is useful for incrementing a loop counter, which we also do here. In our example, we initialize a loop counter to zero, and increment it until the value reaches 10. This ensures that the loop will be run exactly 10 times.

Following the for loop is a section of code contained by \verb|{| and \verb|}|. This gets executed 10 times, and during each execution, the value of {\it i} increments. The single line of code we execute here is a list append operation. The \verb|+| operator here has been overloaded from the usual addition syntax to a list append syntax.

On the first run of the for loop, {\it my\_ints} is an empty list. By appending \verb|[i*20]| to the current value of {\it my\_ints}, we essentially initialize {\it my\_ints} to \verb|[[0]]| (the initial value of {\it i} is 0). On the second run, {\it my\_ints} is already initialized, so instead of creating a new list, we merely append to one. The result after the second loop iteration is \verb|[[0], [20]]|. This pattern continues until the list is full of 10 lists, each with a single integer value that is larger than the previous one by 20.

With all the setup done, we push the job. Note that we have initialize the {\it output} variable, which will be filled with the output contents once the job has finished. The most interesting point, however, is that we are passing inputs to the job. This is reflected by the use of inputs in the job definition.

In the body of the job we run, \verb|job RunFtoC|, we use the usual syntax of simply running a single program, but we use the inputs to run the same job but with different command line inputs on each computer. While we declared the input list, {\it my\_ints}, to be the list \verb|[[0], [20], [40], ...]|, each job only sees one list item. So, for example, on one computer, the job will receive inputs as the list \verb|[120]|. In order to extract the first element of the list, we can use the syntax usually used for array indexing in a language, \verb|inputs[0]|.

In order to execute the program, we must provide a string to execute. However, the string to execute is different for each job instance. For instance, one will need to execute \verb|ftoc 20| on one computer, and \verb|ftoc 160| on another. Thus, we need to be able to do string manipulation. To do this, we adopt the \% operator from Python, which allows for printf-like statements anywhere. In other words, printing the string \verb|"ftoc %s" % inputs[0]| is equivalent to calling \verb|printf("ftoc %s", inputs[0])| in C.

After the job has been pushed to the remote computers, \verb|push| returns. In order to know when it returns, we capture the return value of it, and then call \verb|wait conversion|, which waits for the job with the id specified by {\it conversion} to finish. This call blocks, so execution halts until the job finishes.

After we are done, we want to print the results of the conversion. For this, we iterate over the {\it output} variable with a for loop. The only difference in the for loop declaration is that we test again the loop iterator being less than the length of the output list instead of a constant. We use the \verb|length| command to determine the length of a list (or map).

In order to output a string to standard out, we provide the \verb|print| syntax, which takes a string and sends it to \verb|stdout|. In this example, we use the \% syntax as we did before. In this case, however, there are multiple values we want to format into this string. In the previous example, we had only one value, so we could use the \% operator followed by the variable to fill the formatter with. Here, in order to include several values into the format string, we provide a list to the right of the \% operator.

\section{Distributed C Compiler}
In this section, we implement a barebones version of \verb|distcc|, a program that distributes compilation over the network. It does this by sending out source files, compiling them there, and sending the object files back and linking them together locally. Here we hardcode it, sending out four source files, and linking them to create a \verb|MyCoolProgram| binary.

\begin{lstlisting}[caption=A stripped down distcc.]
job compile(list inputs) {
  @{"gcc -c %s" % inputs[0]};
}

void main(list args) {
  list my_servers = [["host"=>"clic.cs.columbia.edu",
                      "user"=>"jordan",
                      "pass"=>"pigasusR0Ck"
                     ],
                     ["host"=>"compute.cs.columbia.edu",
                      "user"=>"jjr2119",
                      "pass"=>"example"
                     ]];
  connect my_servers;
  
  list srcs = [[(file)"a.c"], [(file)"b.c"], [(file)"c.c"], [(file)"d.c"]];

  list output;
  int distcompile = push compile, null, srcs, output;
  wait distcompile;
  
  string ld = "gcc -o MyCoolProgram";
  
  for (int i = 0; i < length output; i = i + 1) {
    list out = output[i];
    for (int j = 0; j < length out["files"]; j = j + 1) {
      ld = "%s %s/%s" % [ld, out["root"], out["files"][j]];
    }
  }
  
  @{ld};
}
\end{lstlisting}

There are two major differences in this piece of code. The first is in how we implement \verb|push|. We pass \verb|null| in place of the files to send, since the only relevant file which {\bf all} jobs need is \verb|gcc|. We assume this is already on all the computers. However, each job needs the source code it is compiling to be sent. This is implemented by passing the files into the input list. However, in order to let Pigasus know that this is a file we are passing over, we cast it as a \verb|file|. Then, when the job executes, it sees the name of the file as a string, but the file itself will also be sent automatically.

When the job finishes, it will detect which files have been created or modified, and it sends those back. However, in the case that separate jobs might produce files with the same name, they are separated into different directories. Thus, the list of files is represented by the \verb|out["files"]| array, but the files are all in the directory \verb|out["root"]| (which is a directory relative to the current working directory).

Finally, when we have parsed through the entire outputs array, we have then finished the string which we can use to link the program. If, for instance, Pigasus places the files for the four jobs in folders 1, 2, 3, and 4, then the contents of the \verb|ld| string will be \verb|gcc -o MyCoolProgram 1/a.o 2/b.o 3/c.o 4/d.o|. We can then execute this on the local machine by running \verb|@{ld}|.

\section{Alternate syntax}
Here, we experiment with different syntaxes that do essentially the same things as before. Specifically, we provide an example that illustrates keywords \verb|connect|, \verb|while|, and \verb|waitall|.

\begin{lstlisting}[caption=Another Pigasus Program]
job Tests(list inputs) {
  @{"./test1 %s %s %s" % [inputs[0], inputs[1], inputs[2]]};
  @{"java test2 %s %s" % [inputs[3], inputs[4]]};
}

job Combine(list inputs) {
  @{"./combine %s %s %s %s %s" % [inputs[0], inputs[1], inputs[2], inputs[3], inputs[4]] };
}

void main(list args) {
  //declare servers
  list first_servers = [["host"=>"clic.cs.columbia.edu",
                         "user"=>"pmt2103",
                         "pass"=>"pigasusR0Ck"
                         ],
                        ["host"=>"compute.cluster.com",
                         "user"=>"ptjimos"
                         "pass"=>"mypasswd"
                        ]];
  
  list second_servers = [["host"=>"compute.cs.columbia.edu",
                          "user"=>"pmt2103",
                          "pass"=>"computepswd"
                          ],
                         ["host"=>"cunix.cc.columbia.edu",
                          "user"=>"pmt2103"
                          "pass"=>"cunixpwd"
                         ]];
  connect first_servers;
  
  list first_inputs;
  int i = 0;
  while (i < 5) {
    first_inputs = first_inputs + [i, i*5, i*10, i*15, i*20];
    i = i + 1;
  }
  
  list first_output;
  list test_programs = ["/home/test1", "/home/java/test2.class"];
  push Tests, test_programs, first_inputs, first_output;
  waitall;
  
  list results = [null];
  i = 0;
  while (i < length first_output) {
    results[0] = results[0] + final_output["stdout"];
    i = i + 1;	
  }
  connect second_servers;
  
  list final_output;
  list combine_program = ["/home/combine"];
  push Combine, combine_program, results, final_output;
  waitall;
  
  print "Final Result is: " % final_output[0]["stdout"];
}
\end{lstlisting}

The first thing to notice is that we have two separate jobs defined. The first
one, {\it Tests}, illustrates running two commands within one job. These tasks
will be executed sequentially, even though the job instances themselves will be
distributed to other computers in parallel. The first command in {\it Tests}
runs a binary program on inputs, while the second command runs a Java class on
some inputs. This job shows off the flexibility of Pigasus.

In the {\it main} code, we define two different lists of servers, but we initially only connect to one of them. We then begin to prepare the variables for pushing the job. Here, instead of using the usual \verb|for| loop, we implement a \verb|while| loop. Although the two loop types are very similar, sometimes one is more natural than the other. The \verb|while| loop executes the body for as many times as the expression in the parentheses is true. If it is not even true the first time, it will never execute the body; if the statement is a \verb|while (true)| loop, it will never stop executing the loop, unless there is a strong breaking statement such as \verb|break| or \verb|return|.

We now are ready to push the job as usual. Before, we always grabbed the return value of \verb|push| in order to wait for it to finish. However, we can make use of a different keyword, \verb|waitall|, which blocks until any and all jobs that have been pushed so far finish executing. This is convenient in many cases: if we only have one \verb|push| statement, we might as well use \verb|waitall| instead of bothering to retain the return of \verb|push| and use \verb|wait|; if we have many \verb|push| statements, it is a lot easier to say \verb|waitall| instead of calling a \verb|wait| for every \verb|push|.

\section{Comparing Running Times}

We provide the code here for a program that compares the relative performance between two machines, that is, suppose the user owns two computers and wonders which one can execute a given command faster.

\begin{lstlisting}[caption=A program for comparing machine performance.]
job RunRayTracer(list inputs) {
  @{"./raytrace"}
}

void main(list args) {
  list machine1 = [["host" => "compute01.cs.columbia.edu",
                    "user" => "jordan",
                    "pass" => "pigasusR0Ck"
                   ]];
  list machine2 = [["host" => "compute02.cs.columbia.edu",
                    "user" => "jjr2119",
                    "pass" => "example"
                   ]];

  list my_files = [ "raytrace" ];
  list output1, output2;

  connect machine1;
  int job1 = push RunRayTracer, my_files, null, output1;
  connect machine2;
  int job2 = push RunRayTracer, my_files, null, output2;
 
  post job1 {
    print "Machine 1 runs faster!\n";
    return;
  }

  post job2 {
    print "Machine 2 runs faster!\n";
    return;
  }

  print "Jobs running.\n";
}
\end{lstlisting}

This program runs a raytracer binary (written in C using OpenGL) which renders a 3D image and outputs a PPM (portable pixmap) image on two remote machines. The first job to finish will print ``Machine \emph{x} runs faster!'' before exiting the program.

This program demonstrates two key statements of Pigasus: \verb|connect| and \verb|post|. It also shows how the \verb|return| statement is handled in the \verb|main| function.

When a user indicates worker servers to connect to using a list and \verb|connect|, and then subsequently pushes a job, then Pigasus will automatically connect to the machines specified by \verb|connect| and execute those jobs remotely. Even when a second \verb|connect| and subsequent \verb|push| is executed, then without interfering with the first job, Pigasus will connect to the machines specified by the second \verb|connect| and execute the jobs subsequently indicated.

The \verb|post| statement waits for a job to finish before executing the code in the following block. The key difference between \verb|post| and \verb|wait| however, is that \verb|post| runs in the background and will not block. Therefore (assuming the two \verb|RunRayTracer| jobs do not finish instanteously), the previous Pigasus code will print ``Jobs Running!'' before it enters the code in the \verb|post| code blocks.

Finally, note the use of the \verb|return| statement within the \verb|post| blocks. When a \verb|return| statement is reached in a function, that function will terminate and return the specified value (if applicable, which is not the case in the main function as it returns void). Without the \verb|return| statement in the \verb|main| function, the program would continue to run in the background due to the two \verb|post| statements, even after the last line of the \verb|main| function is reached and executed. However, the use of \verb|return| means that the function will terminate. Therefore, when the \verb|return|'s are reached in the \verb|post| blocks, the entire Pigasus program will terminate, and all connections formed during the program will be severed.

\section{Using Functions}
\begin{lstlisting}[caption=A program that uses Euclid's Algorithm]
job TestGcd(list inputs) {
  @{"test %s %s" % [inputs[0], inputs[1]};
}

/* no need for function prototypes */

void main(list args) {
  list server = [["host"=>"clic.cs.columbia.edu",
                  "user"=>"pmt2103",
                  "pass"=>"pigasusR0Ck"
                 ]];
  connect server;

  list inputs;
  inputs = inputs + [ gcd(102,34), gcd(122,42) ];
  inputs = inputs + [ gcd(32,13), gcd(11,3) ];

  int a = gcd(15, 20);
  int b = gcd(12, 16);
  inputs = inputs + [ a, b ];

  list test_program = ["test"];
  push TestGcd, test_program, inputs, null;
}

int gcd(int m, int n) {
  if ((m % n) == 0)
    return n;
  else
    return gcd(n, m % n);
}
\end{lstlisting}

This final example illustrates the use of functions in Pigasus. We adopt the usual C syntax for defining and using functinos, with the exception that there is no need to use function prototypes. To illustrate this, we call a function {\it gcd} within {\it main}, but we only declare it after it.

\newpage
\bibliographystyle{abbrv}
\bibliography{lrm}

\end{document}
